package GUI;

import org.cloudbus.cloudsim.*;
import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.provisioners.BwProvisionerSimple;
import org.cloudbus.cloudsim.provisioners.PeProvisionerSimple;
import org.cloudbus.cloudsim.provisioners.RamProvisionerSimple;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.Comparator;

public class SRTF {
    public static int vmNum = 5;  // 虚拟机数量
    public static int cloudletNum = 10;  // 云任务数量
    private static List<Cloudlet> cloudletList;  // 云任务列表
    private static List<Vm> vmList;  // 虚拟机列表

    public static void main(String[] args) {
        try {
            initializeCloudSim();  // 初始化CloudSim环境
            Datacenter datacenter = createDatacenter("Datacenter_0");  // 创建数据中心
            DatacenterBroker broker = createBroker();  // 创建经纪人
            createAndSubmitVms(broker);  // 创建并提交虚拟机
            createAndSubmitCloudlets(broker);  // 创建并提交云任务
            bindCloudletsToVms(broker);  // 将云任务绑定到虚拟机
            CloudSim.startSimulation();  // 开始模拟
            List<Cloudlet> newList = broker.getCloudletReceivedList();  // 获取处理完成的云任务列表
            CloudSim.stopSimulation();  // 停止模拟
            printCloudletList_1(newList);  // 打印云任务执行结果
            Log.printLine("最短剩余时间优先调度完成！");  // 输出调度完成信息
        } catch (Exception e) {
            e.printStackTrace();
            Log.printLine("参数错误");
        }
    }

    private static void initializeCloudSim() {
        int num_user = 1;  // 用户数量
        Calendar calendar = Calendar.getInstance();  // 获取当前日期
        boolean trace_flag = false;  // 设置是否跟踪
        CloudSim.init(num_user, calendar, trace_flag);  // 初始化CloudSim环境
    }

    private static Datacenter createDatacenter(String name) {
        // 创建数据中心，包含虚拟机的宿主机等信息
        List<Host> hostList = new ArrayList<>();
        int MIPS = 1000;  // MIPS（百万条指令每秒）
        int hostId = 0;
        int ram = 2048;  // RAM大小
        long storage = 1000000;  // 存储空间
        int bw = 10000;  // 带宽

        // 为每台虚拟机创建宿主机
        for (int i = 0; i < vmNum; i++) {
            List<Pe> peList = new ArrayList<>();
            peList.add(new Pe(0, new PeProvisionerSimple(MIPS)));  // 设置每个PE（处理单元）的MIPS
            hostList.add(new Host(hostId,
                    new RamProvisionerSimple(ram),
                    new BwProvisionerSimple(bw),
                    storage,
                    peList,
                    new VmSchedulerTimeShared(peList)
            ));
            hostId++;
        }

        DatacenterCharacteristics characteristics = new DatacenterCharacteristics(
                "x86", "Linux", "Xen", hostList,
                10.0, 3.0, 0.05, 0.001, 0.0
        );

        LinkedList<Storage> storageList = new LinkedList<>();
        try {
            return new Datacenter(name, characteristics,
                    new VmAllocationPolicySimple(hostList),
                    storageList, 0);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static DatacenterBroker createBroker() {
        DatacenterBroker broker = null;
        try {
            broker = new DatacenterBroker("Broker");  // 创建一个经纪人
        } catch (Exception e) {
            e.printStackTrace();
        }
        return broker;
    }

    private static void createAndSubmitVms(DatacenterBroker broker) {
        // 创建并提交虚拟机列表
        int vmid = 0;
        int[] mipss = new int[]{1000, 800, 600, 400, 300};  // 每台虚拟机的MIPS
        long size = 10000;  // 虚拟机大小
        int ram = 2048;  // 虚拟机的RAM大小
        long bw = 1000;  // 带宽
        int pesNumber = 1;  // 每台虚拟机的PE数目
        String vmm = "Xen";  // 虚拟机监视器类型

        vmList = new ArrayList<>();
        for (int i = 0; i < vmNum; i++) {
            Vm vm = new Vm(vmid, broker.getId(), mipss[i], pesNumber, ram, bw, size, vmm, new CloudletSchedulerSpaceShared());
            vmList.add(vm);
            vmid++;
        }

        broker.submitVmList(vmList);  // 提交虚拟机列表
    }

    private static void createAndSubmitCloudlets(DatacenterBroker broker) {
        // 创建并提交云任务列表
        int id = 0;
        long[] lengths = new long[]{10000, 25000, 15000, 30000, 20000, 18000, 21000, 23000, 27000, 24000};  // 云任务长度
        long fileSize = 500;  // 文件大小
        long outputSize = 1000;  // 输出大小
        UtilizationModel utilizationModel = new UtilizationModelFull();  // 完全利用模型

        cloudletList = new ArrayList<>();
        for (int i = 0; i < cloudletNum; i++) {
            Cloudlet cloudlet = new Cloudlet(id, lengths[i], 1, fileSize, outputSize, utilizationModel, utilizationModel, utilizationModel);
            cloudlet.setUserId(broker.getId());
            cloudletList.add(cloudlet);
            id++;
        }

        broker.submitCloudletList(cloudletList);  // 提交云任务列表
    }

    private static void bindCloudletsToVms(DatacenterBroker broker) {
        // 使用SRTF算法对云任务进行排序并绑定到虚拟机
        List<Cloudlet> sortedCloudlets = new ArrayList<>(cloudletList);
        sortedCloudlets.sort(Comparator.comparingLong(Cloudlet::getCloudletLength));

        int cloudletIndex = 0;
        int vmIndex = 0;
        int vmCount = vmList.size();
        int cloudletCount = cloudletList.size();

        while (cloudletIndex < cloudletCount) {
            Cloudlet cloudlet = sortedCloudlets.get(cloudletIndex);
            Vm vm = vmList.get(vmIndex);
            broker.bindCloudletToVm(cloudlet.getCloudletId(), vm.getId());  // 绑定云任务到虚拟机
            cloudletIndex++;
            vmIndex = (vmIndex + 1) % vmCount;  // 循环遍历虚拟机
        }
    }

    private static String printCloudletList_1(List<Cloudlet> list) {
        // 打印云任务的执行结果
        int size = list.size();
        Cloudlet cloudlet;
        String indent = "    ";
        Log.printLine();
        Log.printLine("========== OUTPUT ==========");
        Log.printLine("No." + indent + "Cloudlet ID" + indent + "STATUS" + indent
                + "Data center ID" + indent + "VM ID" + indent + "VM mips" + indent + "Cloudlet Length" + indent + "Time"
                + indent + "Start Time" + indent + "Finish Time");

        DecimalFormat dft = new DecimalFormat("###.##");
        for (int i = 0; i < size; i++) {
            cloudlet = list.get(i);
            Log.print(i + 1 + indent + indent + cloudlet.getCloudletId() + indent + indent);

            if (cloudlet.getStatus() == Cloudlet.SUCCESS) {
                Log.print("SUCCESS");
                Log.printLine(indent + indent + indent + cloudlet.getResourceId()
                        + indent + indent + indent + cloudlet.getVmId()
                        + indent + indent + getVmById(cloudlet.getVmId()).getMips()
                        + indent + indent + cloudlet.getCloudletLength()
                        + indent + indent + indent + indent
                        + dft.format(cloudlet.getActualCPUTime()) + indent
                        + indent + dft.format(cloudlet.getExecStartTime())
                        + indent + indent
                        + dft.format(cloudlet.getFinishTime()));
            }
        }
        return dft.format(list.get(size - 1).getFinishTime());
    }

    public static Vm getVmById(int vmId) {
        // 通过虚拟机ID获取虚拟机对象
        for (Vm v : vmList) {
            if (v.getId() == vmId)
                return v;
        }
        return null;
    }
}
